package com.hzw.core.view

import android.view.View
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import com.hzw.core.log.logE
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

/**
 * 等待对此View]执行单击，然后继续。 此功能是可取消的。
 *
 * 默认情况下， [disableAfterClick] 设置为true ，这将使该视图在调用此函数时启用，最后在单击或取消后被禁用。
 * 请注意，如果将此参数设置为false ，则不会尝试启用该按钮。
 *
 * 默认情况下， [hideAfterClick] 参数设置为false 。
 * 如果启用，则在调用此功能时将使该视图可见，并在单击或取消后将其隐藏起来（对View.GONE的可见性）。
 * 请注意，将不会执行任何转换。 如果要利用TransitionManager （或其AndroidX版本），则需要改用visibleUntilClicked 。
 */
suspend fun View.awaitOneClick(
    disableAfterClick: Boolean = true,
    hideAfterClick: Boolean = false
) = try {
    if (disableAfterClick) isEnabled = true
    if (hideAfterClick) isVisible = true
    suspendCancellableCoroutine<Unit> { continuation ->
        setOnClickListener {
            continuation.resume(Unit) {
                it.logE()
            }
        }
    }
} finally {
    setOnClickListener(null)
    if (disableAfterClick) isEnabled = false
    if (hideAfterClick) isVisible = false
}

/**
 * 与[awaitOneClick]相同，但需要长时间单击。 此功能是可取消的。
 */
suspend fun View.awaitOneLongClick(
    disableAfterClick: Boolean = true,
    hideAfterClick: Boolean = false
) = try {
    if (disableAfterClick) isEnabled = true
    if (hideAfterClick) isVisible = true
    suspendCancellableCoroutine<Unit> { continuation ->
        setOnLongClickListener { continuation.resume(Unit){}; true }
    }
} finally {
    setOnLongClickListener(null)
    if (disableAfterClick) isEnabled = false
    if (hideAfterClick) isVisible = false
}

/**
 * 使该视图可见，暂停直到单击它，执行onClick并在再次隐藏该视图之后返回其结果。
 * 默认情况下，其最终可见性为View.GONE ，但是如果将finallyInvisible设置为true ，
 * 则可见性将改为设置为View.INVISIBLE 。
 * 此功能是可取消的。
 * onClick lambda在此处，因此您可以在View可见性更改之前调用TransitionManager.beginDelayedTransition或其AndroidX版本，
 * 但它也可以适合其他用例，包括返回自定义值。
 */
suspend inline fun <R> View.visibleUntilClicked(
    disableAfterClick: Boolean = true,
    finallyInvisible: Boolean = false,
    onClick: () -> R
): R = visibleInScope(finallyInvisible) {
    awaitOneClick(disableAfterClick = disableAfterClick)
    onClick()
}

suspend inline fun <R> View.visibleUntilLongClicked(
    disableAfterClick: Boolean = true,
    finallyInvisible: Boolean = false,
    onClick: () -> R
): R = visibleInScope(finallyInvisible) {
    awaitOneLongClick(disableAfterClick = disableAfterClick)
    onClick()
}

/**
 * 隐藏该视图后，使该视图可见，执行块并返回其结果。
 * 默认情况下，最终的可见性是View.GONE ，
 * 但如果将finallyInvisible设置为true ，则它将是View.INVISIBLE 。
 * 此功能旨在在协程中使用，该协程中block中至少有一个suspend调用。
 * 如果该块被取消或抛出，则根据finallyInvisible参数最终将可见性更改回原样。
 */
@UseExperimental(ExperimentalContracts::class)
inline fun <R> View.visibleInScope(finallyInvisible: Boolean = false, block: () -> R): R {
    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
    return try {
        isVisible = true
        block()
    } finally {
        visibility = if (finallyInvisible) View.INVISIBLE else View.GONE
    }
}

/**
 * 使此View消失（对View.GONE的可见性），执行块并在返回View后立即返回其结果。
 * 此功能旨在在协程中使用，该协程中block中至少有一个suspend调用。
 * 如果取消或抛出该块，可见性将最终更改回View.VISIBLE 。
 */
@UseExperimental(ExperimentalContracts::class)
inline fun <R> View.goneInScope(block: () -> R): R {
    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
    return try {
        isVisible = false
        block()
    } finally {
        isVisible = true
    }
}

/**
 * 使该视图不可见（对View.INVISIBLE的可见性），执行块并在显示视图后立即返回其结果。
 * 此功能旨在在协程中使用，该协程中block中至少有一个suspend调用。
 * 如果取消或抛出该块，可见性将最终更改回View.VISIBLE 。
 */
@UseExperimental(ExperimentalContracts::class)
inline fun <R> View.invisibleInScope(block: () -> R): R {
    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
    return try {
        isInvisible = true
        block()
    } finally {
        isInvisible = false
    }
}
